#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include <asm/io.h>
#include <linux/sched.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>


//主设备号
int major;
 
 //udev自动创建节点
 static struct class *keyirqdrv_class;
// static struct class_device *keyirqdrv_class_dev;
  
  // 操作管脚为gpio14_2
  // 该管脚采用boot_sel[1:0]硬件配置复用功能，现单板已经配置为gpio，默认高电平，按键按下为低电平
  static void __iomem *key_base; //寄存器基地址
  
#define gpio_phy_base 0x045FD000   //key管脚物理地址
#define gpio_data key_base + 0x010 //GPIO 数据寄存器 ，0b00_0001_0000
#define gpio_dir key_base + 0x400  //GPIO 方向控制寄存器
#define gpio_is key_base + 0x404   //GPIO 中断触发寄存器
#define gpio_ibe key_base + 0x408  //GPIO 双沿触发中断寄存器
#define gpio_iev key_base + 0x40C  //GPIO 触发中断条件寄存器
#define gpio_ie key_base + 0x410   //GPIO 中断屏蔽寄存器
#define gpio_ris key_base + 0x414  //GPIO 原始中断状态寄存器
#define gpio_mis key_base + 0x418  //GPIO 屏蔽状态中断寄存器
#define gpio_ic key_base + 0x41C   //GPIO 中断清除寄存器
 
#define gpio_in 0x00 //配置为输入，1输出 0输入def
#define gpio_out_h 0xFF
#define gpio_out_l 0x00
 
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
/* 中断事件标志, 中断服务程序置1 不进休眠，drv_read将它清0 进休眠*/
static volatile int ev_press = 0;

unsigned int val; //中断值

static atomic_t canopen = ATOMIC_INIT(1);//定义原子变量 canopen = 1

//引脚描述结构体
struct pin_irq_desc
{
unsigned int irq;    //中断号
unsigned int pin;    //中断标志寄存器，有中断产生时为1，无中断时为0
unsigned int number; //编号
char *name;          //名称
};

struct pin_irq_desc pins_desc[] =
{
{44, 0x00, 0x01, "key_irq"},
};

static irqreturn_t irq_interrupt(int irq, void *dev_id)
{
	static unsigned int cnt = 0;
	unsigned int key;
	printk("cnt = %d\n", cnt++);
	printk("irq OKey!\n");
	
	key = ioread8(gpio_data); //读取中断标志位
	if (key == 0x00)
	{
	val = key;
	ev_press = 1;                         //不进去休眠
	printk("Key val :%x\n",val);
	wake_up_interruptible(&button_waitq); //唤醒休眠的进程
	}
	iowrite8(0xFF, gpio_ic); //清除中断
	
	return IRQ_HANDLED;
}
	
static int key_drv_open(struct inode *inode, struct file *filp)
{
	int result = 0;
	if(!atomic_dec_and_test(&canopen))//自减后测试是否为0，为0返回true，否则返回false
	{
	atomic_inc(&canopen);
	return -EBUSY;
	}
	
	iowrite8(gpio_in, gpio_dir); //gpio14_2 配置为输入
	iowrite8(0x00, gpio_is);     //配置电平触发
	iowrite8(0x00, gpio_iev);    //配置下降沿触发或者是低电平触发
	iowrite8(0x00, gpio_ibe);    //配置单边沿触发
	iowrite8(0xFF, gpio_ic);     //清除中断
	iowrite8(0x04, gpio_ie);     //使能gpio13_2中断
	//pins_desc->irq = gpio_to_irq(pins_desc->number);
	
	//request_any_context_irq(); irq_set_irq_type(irq, IRQ_TYPE_NONE);
	
	//Hi3519v101好几组GPIO只有一个中断号，所以这里得是共享中断IRQF_SHARED，作为共享中断，dev用来区分不同的中断
	result =  request_irq(pins_desc->irq, irq_interrupt, IRQF_SHARED, pins_desc->name, (void *)&pins_desc);
	//result =  request_any_context_irq(pins_desc->irq, irq_interrupt, IRQF_SHARED, pins_desc->name, (void *)&pins_desc);
	printk("the result of the request_irq is: %d\n", result);
	printk("set key rising irq mode\n");
	//irq_set_irq_type(pins_desc->irq, IRQ_TYPE_NONE);
	return 0;
}

static int key_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
int err = 0;
//如果没有按键动作, 休眠
wait_event_interruptible(button_waitq, ev_press);

err = __copy_to_user(buf, &val, 1);
ev_press = 0; //数据拷贝给运用之后，一次中断完成，进去休眠

return 0;
}

int key_drv_close(struct inode *inode, struct file *file)
{
atomic_inc(&canopen);//重新恢复canopen的值
free_irq(pins_desc->irq, (void *)&pins_desc);
free_irq(198,NULL);
return 0;
}

static struct file_operations key_drv_fops =
{
.owner = THIS_MODULE,
.open = key_drv_open,
.read = key_drv_read,
.release = key_drv_close,
};

int key_drv_int(void)
{
major = register_chrdev(0, "key_drv", &key_drv_fops);
keyirqdrv_class = class_create(THIS_MODULE, "key_drv");
device_create(keyirqdrv_class, NULL, MKDEV(major, 0), NULL, "key"); //dev/key
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);


key_base = ioremap(gpio_phy_base, 0x1000);

printk("key_data = 0x%x\n", (unsigned int)gpio_data);
printk("key_dir = 0x%x\n", (unsigned int)gpio_dir);
return 0;
}

void key_drv_exit(void)
{
device_destroy(keyirqdrv_class, MKDEV(major, 0));
//device_unregister(keyirqdrv_class_dev);
class_destroy(keyirqdrv_class);
unregister_chrdev(major, "key_drv");
}

module_init(key_drv_int);
module_exit(key_drv_exit);
MODULE_LICENSE("GPL");
